package eu.orangeflash.webapis.xmlrpc.utils
{
	import flash.utils.describeType;
	
	public final class Serialize
	{
		public static const ARRAY:String  = "array";
		public static const BASE64:String = "base64";
		public static const BOOL:String   = "boolean";
		public static const DATE:String   = "dateTime.iso8601";
		public static const DOUBLE:String = "double";
		public static const INT:String    = "int";
		public static const STRING:String = "string";
		public static const STRUCT:String = "struct";
		public static const NULL:String	  = "null";		
		
		/**
		 * Method, creates string representation of XML-RPC request method
		 * 
		 * @usage			<code>Serialize.createMethod("A",1,[1,2],{a:"A",b:"B"})</code>
		 * 
		 * @param	name	String, method name
		 * @param	args	Rest paramenters for method.
		 * 
		 * @return			String representation of XML-RPC request method
		 */
		public static function createMethod(methodName:String, params:Array = null):String
		{
			var result:XML = 
			<methodCall>
			   <methodName/>
			   <params/>
			</methodCall>;
			
			result.methodName.appendChild(methodName);
			
			var methodParams:XMLList = result.params;
			
			for each(var arg:* in params)
			{
				methodParams.appendChild(createParam(arg));
			}
			
			return result.toXMLString();
		}
		
		private static function getType(value:*):String
		{
			var type:String = describeType(value).@name;
			var result:String;
			
			switch(type)
			{
				case "Array":
					result = ARRAY;
					break;
				
				case "Boolean":
					result = BOOL;
					break;
				
				case "Date":
					result = DATE;
					break;
				
				case "Number":
					result = DOUBLE;
					break;
				
				case "int":
					result = INT;
					break;
				
				case "String":
					result = STRING;
					break;
				
				default:
					result = STRUCT;				
			}
			return result;
		}
				
		private static function createStruct(value:Object):XML
		{
			var result:XML = <struct/>
			
			for (var itemName:String in value)
			{
				var item:* = value[itemName];
				var type:String = getType(item);
				
				if(type != STRUCT && type != ARRAY)
				{
					result.appendChild(createMember(createSimpleType(type,item),itemName));
				}else if(type == STRUCT)
				{
					result.appendChild(createMember(createStruct(item),itemName));
				}else if(type == ARRAY)
				{
					result.appendChild(createMember(createArray(item),itemName));
				}
			}
			
			return result;
		}
		
		private static function createArray(value:Array):XML
		{
			var result:XML = 
				<array>
					<data>
					</data>
				</array>;
			
			for each(var item:* in value)
			{
				var type:String = getType(item);

				if(type != STRUCT && type != ARRAY)
				{
					result.data.appendChild(createMember(createSimpleType(type,item)));
				}else if(type == STRUCT)
				{
					result.data.appendChild(createMember(createStruct(item)));
				}else if(type == ARRAY)
				{
					result.data.appendChild(createMember(createArray(item)));
				}
			}
			return result;
		}
		
		private static function createMember(member:XML, name:String = null):XML
		{
			var result:XML = <xml/>;
				
			if(name != null)
			{
				result.setName("member");
				result.appendChild(<name/>);
				result.name.appendChild(name);
				result.appendChild(<value/>);
				result.value.appendChild(member);
			}else
			{
				result.setName("value");
				result.appendChild(member);	
			}			

			return result;
		}
		
		private static function createParam(arg:*):XML
		{
			var type:String = getType(arg);
			
			var result:XML = <param/>;
			
			if(type != STRUCT && type != ARRAY)
			{
				result.appendChild(createMember(createSimpleType(type,arg)));
			}else if(type == STRUCT)
			{
				result.appendChild(createMember(createStruct(arg)));
			}else if(type == ARRAY)
			{
				result.appendChild(createMember(createArray(arg)));
			}
			
			return result;
		}
		
		private static function createSimpleType(type:String, value:*):XML
		{
			var result:XML = <type/>;
				result.setName(type);
			if(type != DATE)
			{
				result.appendChild(value);
			}else{
				var now:Date = new Date();
				result.appendChild(
									   now.getFullYear().toString()+
									   (now.getMonth()+1).toString()+
									   now.getDate().toString()+
									   "T"+
									   now.getHours().toString()+":"+
									   now.getMinutes().toString()+":"+
									   now.getSeconds().toString()
								   );
			}
				
			return result;
		}
	}
}